#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

#include "hash.h"
#include "String.h"

Hash *hash_create (int len)
{
    Hash *phash;
    Conn *conn;
    int i;

    phash = (Hash *)malloc (sizeof (Hash));
    if (phash == NULL)
        return NULL;
    conn = (Conn *)malloc (sizeof(Conn)*len);
    if (conn == NULL)
        return NULL;
    for (i = 0; i < len; i++) {
        conn[i].state = 0;
        conn[i].ip = 0;
        conn[i].uri = NULL;
        conn[i].sock = 0;
        conn[i].buf = NULL;
        conn[i].lasttime = 0;
    }

    phash->n = 0;
    phash->max = len;
    phash->conn = conn;

    return phash;
}

void hash_close (Hash *phash)
{
    int i;

    if (phash == NULL)
        return;
    for (i = 0; i < phash->max; i++) {
        if (phash->conn[i].sock > 0)
            close (phash->conn[i].sock);
        string_del (phash->conn[i].buf);
        phash->conn[i].buf = NULL;
    }

    free (phash->conn);
    free (phash);
}

void hash_free (Hash *phash, int sock)
{
    Conn *conn;

	if (phash == NULL)
		return;
    if (sock >= phash->max)
        return;
    conn = phash->conn+sock;

    if (conn->sock > 0) {
        close (conn->sock);
        conn->state = 0;
        conn->ip = 0;
        conn->uri = NULL;
        conn->sock = 0;
        string_del (conn->buf);
        conn->buf = NULL;
        conn->lasttime = 0;
        phash->n--;
    }
}

static int setnonblocking (int sock)
{
    int opts;

    opts = fcntl (sock,F_GETFL);
    if (opts < 0)
        return -1;
    opts = opts|O_NONBLOCK;
    if (fcntl(sock,F_SETFL,opts) < 0)
        return -1;

    return 0;
}

int hash_alloc (Hash *phash, char *uri, unsigned int ip)
{
    Conn *conn;
    int sock;
    int timeout = 100;

    if (phash == NULL)
        return 0;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock >= phash->max || sock < 0)
        return 0;

    conn = phash->conn+sock;
    conn->state = 0;
    conn->ip = ip;
    conn->uri = uri;
    conn->sock = sock;
    setnonblocking (conn->sock);
    setsockopt(conn->sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
    setsockopt(conn->sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
    conn->buf = string_new ();
    conn->lasttime = time(NULL);
    phash->n++;

    return sock;
}

/* is this socket available */
int hash_check (Hash *phash, int sock)
{
    if (phash == NULL || sock >= phash->max || sock < 0)
        return 0;
    return phash->conn[sock].sock > 0;
}

void hash_checktimeout (Hash *phash, int to)
{
	int t;
	int i;

	if (phash == NULL)
		return NULL;
	t = time(NULL);
	for (i = 0; i < phash->max; i++) {
		if (phash->conn[i].sock <= 0)
			continue;
		if (t - phash->conn[i].lasttime > to)
			hash_free (phash, i);
	}
}

void hash_setstate (Hash *phash, int sock, int state)
{
	int st;

	if (phash == NULL)
		return;

	if (phash->conn[sock].state == state)
		return;
	phash->conn[sock].state = state;
	phash->conn[sock].lasttime = time(NULL);
}